vue 整理面试题

4/5/2020 Vue

# VUE 常见面试整理

# 生命周期钩子函数

beforeCreate(初始化界面前)

created(初始化界面后)

beforeMount(渲染dom前)

mounted(渲染dom后)

beforeUpdate(更新数据前)

updated(更新数据后)

beforeDestroy(卸载组件前)

destroyed(卸载组件后)

钩子函数,其实和回调是一个概念,当系统执行到某处时,检查是否有hook,有则回调。说的更直白一点,每个组件都有属性,方法和事件。所有的生命周期都归于事件,在某个时刻自动执行。生老病死

我们首先需要创建一个实例,也就是在 new Vue ( ) 的对象过程当中,首先执行了init(init是vue组件里面默认去执行的),在init的过程当中首先调用了beforeCreate,然后在injections(注射)和reactivity(反应性)的时候,它会再去调用created。所以在init的时候,事件已经调用了,我们在beforeCreate的时候千万不要去修改data里面赋值的数据,最早也要放在created里面去做(添加一些行为)。

created完成之后,它会去判断instance(实例)里面是否含有“el”option(选项),如果没有的话,它会调用vm.$mount(el)这个方法,然后执行下一步;如果的话,直接执行下一步。紧接着会判断是否含有“template”这个选项,如果的话,它会把template解析成一个render function ,即template编译的过程,结果是解析成了render函数

render函数是发生在beforeMount和mounted之间的,这也从侧面说明了,在beforeMount的时候,$el还只是我们在HTML里面写的节点,然后到mounted的时候,它就把渲染出来的内容挂载到了DOM节点上。这中间的过程其实是执行了render function的内容。

beforeMount在有了render function的时候才会执行,当执行完render function之后,就会调用mounted这个钩子,在mounted挂载完毕之后,这个实例就算是走完流程了。

另外还有 keep-alive 独有的生命周期,分别为 activateddeactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。

# 父子组件生命周期调用顺序

父组件初始化完,再初始化子组件

子组件渲染完,再渲染父组件

同理,组件的销毁操作是先父后子,销毁完成的顺序是先子后父。

# React 和 Vue的区别

  • 设计思想:

    vue的官网中说它是一款渐进式框架,采用自底向上增量开发的设计。

    react主张函数式编程,所以推崇纯组件,数据不可变,单向数据流,当然需要双向的地方也可以手动实现,比如借助 onChange 和 setState 来实现一个双向的数据流。

  • 编写语法:

    Vue推荐的做法是webpack+vue-loader的单文件组件格式,vue保留了html、css、js分离的写法

    React的开发者可能知道,react是没有模板的,直接就是一个渲染函数,它中间返回的就是一个虚拟DOM树,React推荐的做法是 JSX+inline style,也就是把 html 和 css 都写进js里,即all in js

  • 构建工具:

    vue提供了CLI 脚手架,可以帮助你非常容易地构建项目

    React 在这方面也提供了 create-react-app,但是现在还存在一些局限性,不能配置等等

  • 数据绑定:

    vue是实现了双向数据绑定的mvvm框架,当视图改变更新模型层,当模型层改变更新视图层。 在vue中,使用了双向绑定技术,就是View的变化能实时让Model发生变化,而Model的变化也能实时更新到View。

    react是单向数据流,react中属性是不允许更改的,状态是允许更改的。 react中组件不允许通过this.state这种方式直接更改组件的状态。自身设置的状态,可以通过setState来进行更改。 (注意:React中setState是异步的,导致获取dom可能拿的还是之前的内容, 所以我们需要在setState第二个参数(回调函数)中获取更新后的新的内容。)

  • diff算法:

    vue中diff算法实现流程:

    1. 内存中构建虚拟 dom
    2. 将内存中虚拟 dom 渲染成真实 dom
    3. 当数据发生改变时,将之前的虚拟 dom 树结合新的数据生成新的虚拟 dom
    4. 将此次生成好的虚拟dom树和上一次的虚拟dom树进行一次比对(diff算法进行比对),来更新只需要被替换的DOM,而不是全部重绘。在Diff算法中,只平层的比较前后两棵DOM树的节点,没有进行深度的遍历。
    5. 会将对比出来的差异进行重新渲染
    

    react中diff算法实现流程:

    DOM结构发生改变-----直接卸载并重新create
    DOM结构一样-----不会卸载,但是会update变化的内容
    所有同一层级的子节点.他们都可以通过key来区分-----同时遵循1.2两点(其实这个key的存在与否只会影响diff算法的复杂度,换言之,你不加key的情况下,diff算法就会以暴力的方式去根据一二的策略更新,但是你加了key,diff算法会引入一些另外的操作)
    

参考:前端框架用vue还是react?清晰对比两者差异 (opens new window)

# 组件通信

  • 父子组件通信
  • 兄弟组件通信
  • 跨多层级组件通信
  • 任意组件

# 父子

父组件通过 props 传递数据给子组件,子组件通过 emit 发送事件传递数据给父组件,这两种方式是最常用的父子通信实现办法。 单向数据流,父组件通过 props 传递数据,子组件不能直接修改 props, 而是必须通过发送事件的方式告知父组件修改数据。父组件通过v-on监听并接收参数

当然我们还可以通过访问 $parent 或者 $children 对象来访问组件实例中的方法和数据。

v-model

因为 v-model 默认会解析成名为 valueprop 和名为 input 的事件。这种语法糖的方式是典型的双向绑定,常用于 UI 控件上,但是究其根本,还是通过事件的方法让父组件修改数据。

在一个组件上使用v-model,默认会为组件绑定名为value的prop和名为input的事件
1

# 兄弟

# eventBus

初始化:

// event-bus.js

import Vue from 'vue'
export const EventBus = new Vue()

1
2
3
4
5

兄弟组件通过EventBus.$emit分发和EventBus.$on接收

# $children / $parent

对于这种情况可以通过查找父组件中的子组件实现,也就是 this.$parent.$children,在 $children中可以通过组件 name 查询到需要的组件实例,然后进行通信。

# mixin 和 mixins 区别

mixin 用于全局混入,以全局混入封装好的 ajax 或者一些工具函数等等。

mixins 应该是我们最常使用的扩展组件的方式了。如果多个组件中有相同的业务逻辑,就可以将这些逻辑剥离出来,通过 mixins 混入代码,比如上拉下拉加载数据这种逻辑等等。

需要注意的是 mixins 混入的钩子函数会先于组件内的钩子函数执行,并且在遇到同名选项的时候也会有选择性的进行合并,具体可以阅读
1

文档 (opens new window)

# computed 的实现原理

computed 是一个惰性求值的观察者

它的内部实现了一个惰性的 watcher ,即computed wathcer,computed wathcer 不会立即求值,同时拥有一个 dep 实例。内部通过 this.dirty属性来标记计算是否需要重新求值

当 computed 的依赖状态发生改变时,就会通知这个惰性的 watcher,

computed watcher 通过 this.dep.subs.length 判断有没有订阅者,有的话,会重新计算,然后对比新旧值,如果变化了,会重新渲染。 (Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染 watcher 重新渲染,本质上是一种优化。) 没有的话,仅仅把 this.dirty = true。 (当计算属性依赖于其他数据时,属性并不会立即重新计算,只有之后其他地方需要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)特性。)

# computed 和 watch 区别

computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容。内部做了一个一个 dirty ,实现缓存。当依赖的属性发生变化,就会让 dirty 变为true

watch 监听到值的变化就会**执行回调,**在回调中可以进行一些逻辑操作。可设置 deep:true 深层次监听,利用递归实现。

所以一般来说需要依赖别的属性来动态获得值的时候可以使用 computed,对于监听到值的变化需要做一些复杂业务逻辑的情况可以使用 watch

# v-show 与 v-if 区别

v-show 无论初始条件是什么都会被渲染出来,后面只需要切换 CSS,DOM 还是一直保留着的。display:none属性,所以总的来说 v-show 在初始渲染时有更高的开销,但是切换开销很小,更适合于频繁切换的场景。,v-show是控制有没有display:none这个样式来控制显隐。

v-if 条件渲染,切换条件时会触发销毁/挂载组件,所以总的来说在切换时开销更高,更适合不经常切换的场景。

# vue中组件的data为什么是一个函数

组件是可复用的vue实例,一个组件被创建好之后,就可能被用在各个地方,而组件不管被复用了多少次,组件中的data数据都应该是相互隔离,互不影响的。

组件中的data 写成函数,数据以函数返回值定义,这样每复用一次组件,就能够获得一份独立的 data 拷贝,互不影响。原因在于采用函数定义,在 initData 时会将其作为工厂函数返回全新的 data 对象,有效规避多组件间的状态污染。

而在 Vue 根实例创建则不存在此限制,因为根实例只有一个。无须担心此状况。
1

# vue 插槽的使用

  • 匿名插槽
  • 具名插槽
  • 作用域插槽

具体用法可参考 (opens new window)slot详解 (opens new window)从一个简单的 list 组件搞懂 Vue 插槽 (opens new window)

# nextTick的用途与原理

应用场景 :需要在试图更新后,基于新的视图进行操作。

例:点击按钮让原本隐藏的输入框显示,并且获取焦点

<input v-if='isShow' id='keywords'>
<button @click="showInput">输入框显示,并且获取焦点</button>

showInput() {
	this.isShow=true
	// document.getElementById('keywords').focus //获取不到输入框报错
	this.$nextTick(function () {
    // DOM 更新了,可以获取
    document.getElementById("keywords").focus()//加上$nextTick 
}

1
2
3
4
5
6
7
8
9
10
11

vue 的数据响应通过 Object.defineProperty 实现,而 vue 更新 dom 是异步的,官方这样解释:

Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的Promise.then和 MessageChannel,如果执行环境不支持,会采用setTimeout(fn, 0)代替。

即 nextTick 原理与 Event Loop 相关

事件循环又分为

macro-task(宏任务)包括:

  • script(整体代码)
  • setTimeout / setInterval
  • setImmediate(Node.js 环境)
  • I/O
  • UI render
  • postMessage
  • MessageChannel

不建议记住宏任务,太多,记住以下微任务即可,其他不知道的把其归类为宏任务(当然,此仅为面试时技巧)

micro-task(微任务):

  • process.nextTick(Node.js 环境)
  • Promise
  • Async/Await
  • MutationObserver(html5 新特性)

所有微任务都会在下一次渲染前完成,目的是在渲染前更新应用程序状态。

关于事件循环执行顺序总结与速记: 先执行主线程 遇到宏队列(macrotask)放到宏队列(macrotask) 遇到微队列(microtask)放到微队列(microtask) 主线程执行完毕 执行微队列(microtask),微队列(microtask)执行完毕 执行一次宏队列(macrotask)中的一个任务,执行完毕 执行微队列(microtask),执行完毕 依次循环。。。

nextTick 的主要实现依赖于 微任务,但 Vue 为了做好一些兼容,优先使用 promise ,其次是 html5 的 MutationObserver,然后是setTimeout。前两者属于microtask,后一个属于 macrotask。

优先尝试 Promise ,尝试 MutationObserver,尝试 setImmediate,最终不行在使用 setTimeout

参考:从 javascript 事件循环看 Vue.nextTick 的原理和执行机制 (opens new window)

# Proxy 与 Object.defineProperty 的优劣对比

proxy 优势 :

  • 能直接监听数组的变化
  • 能直接监听对象而非属性
  • 多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has等等是 Object.defineProperty 所不具备的
  • 返回一个新对象,我们的操作只对于新对象,而 Object.defineProperty 只能遍历对象属性进行更改
  • 性能红利
  • Object.defineProperty 会直接改变原始数据,而 Proxy 不会直接修改原始数据

在Vue中,Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。 为了解决这个问题,经过vue内部处理后可以使用以下几种方法来监听数组 (其实,Object.defineProperty本身是可以监控到数组下标的变化的,具体可参Vue为什么不能检测数组变动 (opens new window)

# 观察者模式与发布-订阅模式的区别

观察者模式

观察者模式是一种行为模式,它定义了一种一对多的依赖关系。让多个观察者对象同时监听某一个主体对象,这个主体对象发生状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

发布-订阅模式

发布-订阅模式,发布消息的一方叫做发布者,消息不会发送给特定的接收者,而是通过一个第三方组件,也叫作信息中介,意思是发布者和订阅者并不知道对方的存在。这个信息中介把发布者和订阅者串联起来,由它来过滤和分配所有输入的消息。

差异总结

  • 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。

  • 发布-订阅模式中,组件时松散耦合的,和观察者模式相反

  • 观察者模式大多数时候是同步的,例如当事件触发,Subject就会去调用观察者的方法,而发布-订阅模式大多数时候是异步的(使用消息队列)。

  • 观察者 模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式

# v-for和v-if一起使用

v-for和v-if同时使用时,有一个先后运行的优先级。因为v-for比v-if优先级更高,所以当两者用于同一标签时,v-for的每次循环中都会调用v-if。如果要遍历的数组很大,而真正要展示的数据很少时,这将造成很大的性能浪费。

解决方法:ul和li搭配使用,或者是渲染父级标签下的子标签

<ul v-if="data">
	<li v-for="(item,id) in list" :key="id"></li>
</ul>
1
2
3

或使用过滤器将v-if中的判断转移到vue的computed的计算属性中

<ul>
	<li v-for="(item,id) in list" :key="id"></li>
</ul>
 
//利用vue的计算属性,过滤掉不需要的数据,剩下需要的数据再利用v-for循环遍历取出渲染
computed: {
	list: function () {
		return this.list.filter(function (item) {
			return item.state
		})
	}
}
1
2
3
4
5
6
7
8
9
10
11
12

# v-for中的key不建议使用index

我们使用 v-for 时,其内部 diff 算法使用就地复用原则,当列表数据发生更改,他是根据 key 值来判断某个值是否修改,如修改,则重新渲染,否则就复用这一项。key的作用也主要是为了高效的更新虚拟DOM,而 index 则不能做到。

也就是说如果你的列表顺序会改变,别用 index 作为 key,和没写基本上没区别,因为不管你数组的顺序怎么颠倒,index 都是 0, 1, 2 这样排列,导致 Vue 会复用错误的旧子节点,做很多额外的工作。列表顺序不变也尽量别用,可能会误导新人。 具体参考: v-for中的key (opens new window)

# 了解 Virtual DOM?为什么 Virtual DOM 比原生 DOM 快?

Virtual DOM 其实就是通过对文档中的 DOM 树结构进行分析,利用 js 对象将其表示出来,比如一个元素对象,包含 TagName、props 和 Children 这些属性。然后我们将这个 js 对象树给保存下来,最后再将 DOM 片段插入到文档中。

当我们的页面要发生改变,也就是页面 DOM 结构调整,我们首先根据变更的状态,重新构建起一棵对象树,然后将新树和旧树进行对比,记录下两棵树的差异。

最后将有差异的地方进行替换,应用到真正的 DOM 树中,然后视图也就完成更新。

这种方法在我们需要大量的 DOM 操作时,能够很好的提高我们的操作效率,通过在操作前确定需要做的最小修改,尽可能的减少 DOM 操作带来的重流和重绘的影响。其实 Virtual DOM 并不一定比我们真实的操作 DOM 要快,这种方法的目的是为了提高我们开发时的可维护性,在任意的情况下,都能保证一个尽量小的性能消耗去进行操作。

# 如何比较两个 DOM 树的差异?

两个树的完全 diff 算法的时间复杂度为 O(n^3) ,但是在前端中,我们很少会跨层级的移动元素,所以我们只需要比较同一层级的元素进行比较,这样就可以将算法的时间复杂度降低为 O(n)。

算法首先会对新旧两棵树进行一个深度优先的遍历,这样每个节点都会有一个序号。在深度遍历的时候,每遍历到一个节点,我们就将这个节点和新的树中的节点进行比较,如果有差异,则将这个差异记录到一个对象中。

在对列表元素进行对比的时候,由于 TagName 是重复的,所以我们不能使用这个来对比。我们需要给每一个子节点加上一个 key,列表对比的时候使用 key 来进行比较,这样我们才能够复用老的 DOM 树上的节点。

diff 过程整体遵循 **深度优先、同层比较**的策略:两个节点比较他们是否拥有子节点或者文本节点做不同的操作,当比较两组子节点时,会先假设头尾节点相同做四次比较尝试,如果没有找到相同节点则按照通用方式遍历查找,查找结束再按情况处理剩下的节点,借助 key 通常可以更高效精确的找到相同节点,然后复用,因此整个 patch 过程也非常的高效。
1

# Vue的渲染过程

  1. 把模板编译为 render 函数
  2. 实例进行挂载,执行 render 函数,生成 vnode
  3. 基于 vnode 执行 diff 算法,对比虚拟 dom,渲染真实 dom
  4. 组件内的 data 发生变化时,重新调用 render 函数,生成 vnode ,然后又回到步骤3了。

# hash路由和history路由

  • hash模式:依靠onhashchange事件(监听location.hash的改变)
  • history模式:history.pushState 和 replaceState ,pushState()可以改变url地址且不会发送请求,replaceState()可以读取历史记录栈,还可以对浏览器记录进行修改。

# Vue 项目的一些优化?

代码层面优化:

  • v-if 和 v-show 区分使用
  • v-for 加key ,避免同时使用 v-if
  • 路由懒加载
  • 图片懒加载 v-lazy
  • 事件及时销毁
  • 第三方插件按需引入

Webpack 层面:

  • 对图片进行压缩
  • 模板预编译
  • 优化 SourceMap
  • 提取公共代码

基础的 Web 技术的优化:

  • 开启 gzip
  • 使用 cdn
  • 浏览器缓存
  • 使用 Chrome Performance 查找性能瓶颈

具体各优化方法 (opens new window)

# 为什么使用异步组件

项目过大时,核心页面访问速度变慢,使用异步组件将代码分割成小块,需要使用这个组件时在引入,可提高加载的速度。主要依赖import()这个语法。 用法示例:

export default {
    components: {
        Home: () => import('./components/Home')
    }
}
1
2
3
4
5

异步组件的渲染本质上其实就是执行2次或者2次以上的渲染, 先把当前组件渲染为注释节点, 当组件加载成功后, 通过 forceRender 执行重新渲染。或者是渲染为注释节点, 然后再渲染为loading节点, 在渲染为请求完成的组件

# components和 Vue.component

  • components:局部注册组件
export default{
  components:{home}
}
1
2
3
  • Vue.component:全局注册组件
Vue.component('home',home)
1

# 如何在不刷新页面的情况下,刷新组件?

方法:

  • v-if
  • $forceUpdate()
  • v-key

# 路由懒加载

路由懒加载就是把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件。

实现:使用命名 chunk ,和 webpack 的魔法注释

chunkconst Home = () => import(/* webpackChunkName: "group-home" */ './Home.vue')
1

# vue路由按需加载

  1. 写两个 router-view出口
<keep-alive :include="include">
    <!-- 需要缓存的视图组件 -->
  <router-view v-if="$route.meta.keepAlive">
  </router-view>
</keep-alive>

<!-- 不需要缓存的视图组件 -->
<router-view v-if="!$route.meta.keepAlive">
</router-view>
1
2
3
4
5
6
7
8
9
  1. 在Router里定义好需要缓存的视图组件
new Router({
    routes: [
        {
            path: '/',
            name: 'index',
            component: () => import('./views/keep-alive/index.vue'),
            meta: {
                deepth: 0.5
            }
        },
        {
            path: '/list',
            name: 'list',
            component: () => import('./views/keep-alive/list.vue'),
            meta: {
                deepth: 1
                keepAlive: true //需要被缓存
            }
        },
        {
            path: '/detail',
            name: 'detail',
            component: () => import('./views/keep-alive/detail.vue'),
            meta: {
                deepth: 2
            }
        }
    ]
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  1. 在app.vue里监听路由的变化
export default {
  name: "app",
  data: () => ({
    include: []
  }),
  watch: {
    $route(to, from) {
      //如果 要 to(进入) 的页面是需要 keepAlive 缓存的,把 name push 进 include数组
      if (to.meta.keepAlive) {
        !this.include.includes(to.name) && this.include.push(to.name);
      }
      //如果 要 form(离开) 的页面是 keepAlive缓存的,
      //再根据 deepth 来判断是前进还是后退
      //如果是后退
      if (from.meta.keepAlive && to.meta.deepth < from.meta.deepth) {
        var index = this.include.indexOf(from.name);
        index !== -1 && this.include.splice(index, 1);
      }
    }
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

详情 (opens new window)

# keep-alive原理

详细 (opens new window)

# Vue-router 导航守卫有哪些

  • 全局钩子:beforeEach、beforeResolve、afterEach
  • 路由独享守卫:beforeEnter
  • 组件内守卫: beforeRouteEnter 、beforeRouteUpdate、beforeRouteLeave

# vue-router 传参

query方式传参和接收参数

传参: 
this.$router.push({
        path:'/xxx',
        query:{
          id:id
        }
      })
  
接收参数:
this.$route.query.id
1
2
3
4
5
6
7
8
9
10

params方式传参和接收参数

传参: 
this.$router.push({
        name:'xxx',
        params:{
          id:id
        }
      })
  
接收参数:
this.$route.params.id
1
2
3
4
5
6
7
8
9
10

params传参,push里面只能是 name:'xxxx',不能是path:'/xxx',因为params只能用name来引入路由,如果这里写成了path,接收参数页面会是undefined!!! query 和 params 传参方式的区别

  • query 地址栏显示参数,params 则不会,
  • params只能用name来引入路由,query用name,path都可以
  • params参数要在路由中声明了才不会消失。

详细:导航守卫 (opens new window)query 和 params (opens new window)

# $route 和 $router 的区别?

$route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。

而 $router 是“路由实例”对象包括了路由的跳转方法,钩子函数等。

# 组件渲染和更新过程

渲染组件时,会通过 Vue.extend 方法构建子组件的构造函数,并进行实例化。最终手动调用 $mount() 进行挂载,更新组件时会 进行 patchVnode 流程,核心是 diff 算法。

# provide / inject 使用

provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。
1

这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深

//父级组件 a.vue
export default {
  name: "VmRadioGroup",
  props: {
    value: null
  },
  provide() {
    return {
      RadioGroup: this
    };
  }
};

// 子组件注入
export default {
  name: "VmRadio",
  inject: {
    RadioGroup: {
      default:  ""
    }
  },
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 简单介绍 一下 Vuex

Vuex 是一个专门为 Vue 应用程序开发的状态管理工具,每一个 Vuex 应用的核心是 store。Vuex 主要应用了单例模式,这样不管我们尝试去创建多少次,它都只会返回第一次创建的哪一个唯一的实例。

Vuex 主要包含一下几个模块:

  • State:定义应用状态的数据结构
  • Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
  • Mutation:唯一更改 store 中状态的方法,且必须为同步
  • Action:用于 (commit) mutation,可包含任意异步操作
  • Module:可将单一 Store 拆分为多个 store 且保存在单一状态树中

# 为什么 Vuex 的 mutation 中不能做异步操作

Vuex 中所有的状态更新的唯一途径都是通过 mutation,异步通过 Action 来提交 mutation实现,这样可以使我们方便地追踪每一个状态的变化。 如果 mutation 支持异步操作,那么就没有办法知道状态何时更新,,不能很好地追踪状态的变化,也给我们的调试带来困难。

# vnode

参考:Vue Diff算法 (opens new window)

v-for 优先级比 v-if要高,因此可在 v-for里再做if判断

避免出现多的div dom结构,可把 v-for 层的div标签换成 template(template相当于占位)
1

它是可以显示template标签中的内容,但是查看后台的dom结构不存在template标签。如果template标签不放在vue实例绑定的元素内部默认里面的内容不能显示在页面上(即id="app"),但是查看后台dom结构存在template标签。

关于template标签用法总结 (opens new window)

Last Updated: 5/9/2023, 4:20:29 PM